package com.shelpe.services.simbasync.service.impl;


import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.apache.commons.lang.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.shelpe.services.simbasync.dao.CampaignsMapper;
import com.shelpe.services.simbasync.dao.RptCampaignMapper;
import com.shelpe.services.simbasync.exception.ApiException;
import com.shelpe.services.simbasync.model.Campaigns;
import com.shelpe.services.simbasync.model.CampaignsExample;
import com.shelpe.services.simbasync.model.RptCampaign;
import com.shelpe.services.simbasync.model.RptCampaignExample;
import com.shelpe.services.simbasync.service.CampaignsService;
import com.shelpe.services.simbasync.service.simbaproxy.CampaignsProxy;
import com.shelpe.services.simbasync.util.Util;

@Service
public class CampaignsServiceImpl implements CampaignsService {
	
	private static final Logger logger = LoggerFactory.getLogger(CampaignsServiceImpl.class);
	
	//private final ExecutorService executor = Executors.newCachedThreadPool();
	
	@Autowired
	private CampaignsProxy proxy;
	
	@Autowired
	private CampaignsMapper mapper;
	
	@Autowired
	private RptCampaignMapper rptcammapper;
	
	@Override
	public int countAll(){
		
		CampaignsExample example = new CampaignsExample();
		
		return mapper.countByExample(example);
	}
	
	@Override
	public int countByUserId(long userId){
		
		CampaignsExample example = new CampaignsExample();
		example.createCriteria().andUserIdEqualTo(userId);
		
		return mapper.countByExample(example);
	}
	
	@Override
	public int addCampaigns(Campaigns cam) {
		// TODO Auto-generated method stub
		return mapper.insert(cam);
	}

	@Override
	public int addRptCampaigns(RptCampaign rpt) {
		// TODO Auto-generated method stub
		return rptcammapper.insert(rpt);
	}
	
	@Override
	public int deleteRptCampaignsByDateRange(List<Long> userIds, Date startDate, Date endDate){
		RptCampaignExample example = new RptCampaignExample();
		example.createCriteria()
		.andDateBetween(startDate, endDate)
		.andTaobaoUserIdIn(userIds);
		
		return rptcammapper.deleteByExample(example);
	}
	
	@Override
	public int deleteRptCampaignsByDateRangeCamIds(List<Long> campaignIds, Date startDate, Date endDate){
		RptCampaignExample example = new RptCampaignExample();
		example.createCriteria()
		.andDateBetween(startDate, endDate)
		.andCampaignIdIn(campaignIds);
		
		return rptcammapper.deleteByExample(example);
	}

	@Override
	public int addRptCampaignsList(List<RptCampaign> rptList) {
		// TODO Auto-generated method stub
		int num = 0;
		for(RptCampaign item : rptList){
			num += rptcammapper.insert(item);
		}
		return num;
	}
	
	public int insertRptCampaignsBatch(List<RptCampaign> rptList){
		int num = 0;
		int pageSize = 15000;
		if(rptList != null && rptList.size() > 0){
			int fromIndex = 0;
			int endIndex = 0;
			do{
				endIndex += pageSize; 
				if(endIndex > rptList.size()){
					endIndex = rptList.size();
				}
				num += rptcammapper.insertBatch(rptList.subList(fromIndex, endIndex));	
				fromIndex += pageSize; 
			
			}while(fromIndex < rptList.size());
		}
		
		return num;
	}

	@Override
	public List<Campaigns> parseJsonToCampaigns(String response) {

		List<Campaigns> list = new ArrayList<Campaigns>();
		
		if(response != null && response.contains("simba_campaigns_get_response")){
			JSONObject object = JSON.parseObject(response);
			JSONObject objRes = object==null? null : object.getJSONObject("simba_campaigns_get_response");
			JSONObject objItems = objRes==null? null : objRes.getJSONObject("campaigns");
			JSONArray jsonList = objItems==null? null : objItems.getJSONArray("campaign");
			int size = jsonList == null? 0 : jsonList.size();
		
			for(int i = 0; i < size; i++){
				JSONObject item = jsonList.getJSONObject(i);
				//解析
				Campaigns ca = new Campaigns();
			
				ca.setCampaignId(item.getLong("campaign_id"));
				ca.setCreateTime(item.getDate("create_time"));
				ca.setModifiedTime(item.getDate("modified_time"));
				ca.setNick(item.getString("nick"));
				ca.setOnlineStatus(item.getString("online_status"));
				String settleReason = item.getString("settle_reason");
				settleReason = settleReason == null ? "" : settleReason;
				ca.setSettleReason(settleReason);
				ca.setSettleStatus(item.getString("settle_status"));
				ca.setTitle(item.getString("title"));  //其他字段需要从其他api获取，并填充
				
				list.add(ca);
			}	
		}else{
			logger.warn("can not get any info from proxy, maybe something is wrong, api is parseJsonToCampaigns, response: {}", response);
			throw new ApiException();
		}
		
		return list;
	}
	
	@Override
	public void updateCampaignsData(long userId, List<Campaigns> list, boolean singleThread){
		
		Date updatedTime = new Date();
		
		for(Campaigns item : list){
			
			item.setUserId(userId);
			item.setUpdatedTime(updatedTime);
			
			item.setAutoType((short)0);//自动推广类型
			item.setLimit(0);//自动推广限额
			item.setUseSmooth("true");
			
			item.setBudget(0);
			item.setArea("");
			item.setSchedule("");
			item.setIncrementalPrice("");
			item.setPlatform("");	
		}
		
		if(singleThread){
			for(Campaigns item : list){
				taskProcessPerThread(item);
			}
		}
		else{
			ExecutorService executor = Executors.newFixedThreadPool(4);
			list = processTaskByThreadPool(list, executor);
		}
	}

	@Override
	public List<RptCampaign> parseJsonToRptCampaignsBase(String response) {
		// TODO Auto-generated method stub
		List<RptCampaign> ret = new ArrayList<RptCampaign>();
		
		if(response != null && response.contains("simba_rpt_campaignbase_get_response")){
			JSONObject object = JSON.parseObject(response);
			JSONObject objRes = object==null? null : object.getJSONObject("simba_rpt_campaignbase_get_response");
			JSONArray jsonList = objRes==null? null : objRes.getJSONArray("rpt_campaign_base_list");
			int size = jsonList == null? 0 : jsonList.size();
			
			for(int i = 0; i < size; i++){
				JSONObject item = jsonList.getJSONObject(i);
				RptCampaign rpt = new RptCampaign();
				
				int impression = NumberUtils.toInt(item.getString("impressions"), 0);
				int cost = NumberUtils.toInt(item.getString("cost"), 0);
				int click = NumberUtils.toInt(item.getString("click"), 0);
				int aclick = NumberUtils.toInt(item.getString("aclick"), 0);
				float ctr = NumberUtils.toFloat(item.getString("ctr"), 0.0f);
				float cpc = NumberUtils.toFloat(item.getString("cpc"), 0.0f);
				float cpm = NumberUtils.toFloat(item.getString("cpm"), 0.0f);
				float avgpos = NumberUtils.toFloat(item.getString("avgpos"), 0.0f);
				
				rpt.setCpc(cpc);
				rpt.setCtr(ctr) ;
				rpt.setImpressions(impression) ;
				rpt.setCpm(cpm);
				rpt.setClick(click);
				rpt.setCost(cost) ;
				rpt.setCampaignId(item.getLong("campaignid"));
				rpt.setDate(item.getDate("date"));
				rpt.setSearchType(item.getByte("searchtype"));
				rpt.setSource(item.getByte("source"));
				rpt.setAvgpos(avgpos);
				rpt.setAclick(aclick);
				
				rpt.setDirectPay(0);
				rpt.setIndirectPay(0); 
				rpt.setDirectPayCount(0);
				rpt.setIndirectPayCount(0);
				rpt.setFavShopCount(0);
				rpt.setFavItemCount(0); 	
				
				ret.add(rpt);
			}
		}else{
			logger.warn("can not get any info from proxy, maybe something is wrong, api is parseJsonToRptCampaignsBase, response: {}", response);
			throw new ApiException();
		}
		return ret;
	}

	@Override
	public Map<String, List<Campaigns>> splitCampaigns(List<Campaigns> newList, List<Campaigns> oldList) {
		// TODO Auto-generated method stub
		Map<String, List<Campaigns> > ret = new HashMap<String, List<Campaigns> >();
		List<Campaigns> updated = new ArrayList<Campaigns>();
		List<Campaigns> deleted = new ArrayList<Campaigns>();
		List<Campaigns> added = new ArrayList<Campaigns>();
		Set<Long> newCamIds = new HashSet<Long>();
		int oldSize = oldList == null? 0 : oldList.size();
		int i = 0;
		//logger.info("newsize {}, old size {}", newList.size(), oldSize);
		for(Campaigns newItem : newList){
			
			for(i = 0; i < oldSize; i++){
				Campaigns oldItem = oldList.get(i);
				if(newItem.getCampaignId().equals(oldItem.getCampaignId())){
					//比较其他字段是否相同
					if(!isEqualCampaigns(newItem, oldItem)){
						if(oldItem.getAutoType() != null){
							newItem.setAutoType(oldItem.getAutoType());
						}
						if(oldItem.getLimit() != null){
							newItem.setLimit(oldItem.getLimit());
						}
						updated.add(newItem);
					}
					break;
				}
			}
			if(i == oldSize){
				added.add(newItem);
			}
			newCamIds.add(newItem.getCampaignId());
		}
		
		for(Campaigns oldItem : oldList){
			if(!newCamIds.contains(oldItem.getCampaignId())){
				deleted.add(oldItem);
			}
		}
		ret.put("delete", deleted);	
		ret.put("update", updated);	
		ret.put("add", added);	
				
		return ret;
	}
	
	private boolean isEqualCampaigns(Campaigns newAd, Campaigns oldAd){
		
		if(newAd.getSettleReason().equals(oldAd.getSettleReason()) &&
				newAd.getModifiedTime().getTime() == oldAd.getModifiedTime().getTime() &&
				newAd.getSettleStatus().equals(oldAd.getSettleStatus()) &&
				newAd.getBudget().equals(oldAd.getBudget()) &&
				newAd.getUseSmooth().equals(oldAd.getUseSmooth()) &&
				newAd.getArea().equals(oldAd.getArea()) &&
				newAd.getPlatform().equals(oldAd.getPlatform()) &&
				newAd.getSchedule().equals(oldAd.getSchedule()) &&
				newAd.getOnlineStatus().equals(oldAd.getOnlineStatus())){
			return true;
		}
		
		return false;
	}

	@Override
	public List<Campaigns> getCampaignsByNick(String nick) {
		// TODO Auto-generated method stub
		CampaignsExample exp = new CampaignsExample();
		exp.createCriteria().andNickEqualTo(nick);
		return mapper.selectByExample(exp);
	}
	
	@Override
	public List<Campaigns> getCampaignsByUserId(Long userId){
		CampaignsExample exp = new CampaignsExample();
		exp.createCriteria().andUserIdEqualTo(userId);
		return mapper.selectByExample(exp);
	}
	@Override
	public Campaigns getCampaignById(long campaignId){
		
		return mapper.selectByPrimaryKey(campaignId);
	}

	@Override
	public int deleteCampaigns(Campaigns ad) {
		// TODO Auto-generated method stub
		return mapper.deleteByPrimaryKey(ad.getCampaignId());
	}

	@Override
	public int deleteCampaignsList(List<Campaigns> caList) {
		// TODO Auto-generated method stub
		int num = 0;
		for(Campaigns ca : caList){
			num += deleteCampaigns(ca);
		}
		return num;
	}

	@Override
	public int updateCampaigns(Campaigns ad) {
		// TODO Auto-generated method stub
		mapper.updateByPrimaryKey(ad);
		return 0;
	}

	@Override
	public int updateCampaignsList(List<Campaigns> adList) {
		// TODO Auto-generated method stub
		int num = 0;
		for(Campaigns ca : adList){
			num += mapper.updateByPrimaryKey(ca);
		}
		return num;
	}

	@Override
	public int addCampaignsList(List<Campaigns> list) {
		// TODO Auto-generated method stub
		int num = 0;
		for(Campaigns cam : list){
			num += mapper.insert(cam);
		}
		return num;
	}

	@Override
	public Date getLastTimeFromRpt(long userId) {
		// TODO Auto-generated method stub
		return rptcammapper.getLastTimeFromRpt(userId);
	}
	
	@Override
	public Date getLastTimeFromRptByCampaign(long userId, long campaignId){
		
		return rptcammapper.getLastTimeFromRptByCampaign(userId, campaignId);
	}

	@Override
	public int combineRptCampaignEffect(String response, Map<String, Integer> lastDayRecord, List<RptCampaign> list) {
		// TODO Auto-generated method stub
		int size = 0;
		int lastDayRecordTotal = 0;
		int lastDayNoDataNum = 0;
		if(response != null && response.contains("simba_rpt_campaigneffect_get_response")){
			JSONObject object = JSON.parseObject(response);
			JSONObject objRes = object==null? null : object.getJSONObject("simba_rpt_campaigneffect_get_response");
			JSONArray jsonList = objRes==null? null : objRes.getJSONArray("rpt_campaign_effect_list");
			size = jsonList == null? 0 : jsonList.size();
			Date lastDay = Util.getLastDayYYYYMMDD();
			
			for(int i = 0; i < size; i++){
				JSONObject item = jsonList.getJSONObject(i);
				
				if(item.getDate("date").getTime() == lastDay.getTime()){//前一天的数据会出现这种情况
					lastDayRecordTotal++;
					if(!(item.containsKey("indirectpay") || item.containsKey("favItemCount") || item.containsKey("indirectpaycount") ||
							item.containsKey("favShopCount") || item.containsKey("directpay") || item.containsKey("directpaycount"))){
						//这些都没有，认为没有数据，
						lastDayNoDataNum++;
						continue;
					}
				}
				
				int indirectpay = NumberUtils.toInt(item.getString("indirectpay"), 0);
				int	favItemCount = NumberUtils.toInt(item.getString("favItemCount"), 0);
				int	indirectpaycount = NumberUtils.toInt(item.getString("indirectpaycount"), 0);
				int	favShopCount = NumberUtils.toInt(item.getString("favShopCount"), 0);
				int	directpay = NumberUtils.toInt(item.getString("directpay"), 0);
				int	directpaycount = NumberUtils.toInt(item.getString("directpaycount"), 0);
				
				//根据key找到相关的
				for(RptCampaign rpt : list){	
					
					if(rpt.getCampaignId().equals(item.getLong("campaignid")) &&
							rpt .getSource().equals(item.getByte("source")) &&
							rpt.getSearchType().equals(item.getByte("searchtype")) && 
							rpt.getDate().getTime() == item.getDate("date").getTime()){
					
						rpt.setDirectPay(directpay) ;
						rpt.setIndirectPay(indirectpay); 
						rpt.setDirectPayCount(directpaycount);
						rpt.setIndirectPayCount(indirectpaycount);
						rpt.setFavShopCount(favShopCount) ;
						rpt.setFavItemCount(favItemCount); 	
						
						break;
					}
				}		
			}
		}else{
			logger.warn("can not get any info from proxy, maybe something is wrong, api is combineRptCampaignEffect, response: {}", response);
			throw new ApiException();
		}
		lastDayRecord.put("total", lastDayRecordTotal);
		lastDayRecord.put("lost", lastDayNoDataNum);

		return size;
	}
	
	@Override
	public void updateRptCampaignsData(long userId, List<RptCampaign> list){
	
		for(RptCampaign rpt : list){
			rpt.setTaobaoUserId(userId);
		}
		//extra data
	}
	

	private List<Campaigns> processTaskByThreadPool(List<Campaigns> listSpit, ExecutorService executor){
		
		CompletionService<Campaigns> comletionService = new ExecutorCompletionService<Campaigns>(executor); 
		
		for(final Campaigns item : listSpit){
			comletionService.submit(new Callable<Campaigns>(){

				@Override
				public Campaigns call() throws Exception {
					// TODO Auto-generated method stub
					return taskProcessPerThread(item);
				}});
		}
		
		List<Campaigns> results = new ArrayList<Campaigns>();
		
		try{
			int size = listSpit.size();
			
			for(int i = 0; i < size; i++){
				Future<Campaigns> f = comletionService.take();
				Campaigns result = f.get();
				if(result != null){
					results.add(result);
				}
				
			}
			
			return results;
		}catch(InterruptedException e){
			//do something
			logger.warn("something is wrong InterruptedException");
		}catch(ExecutionException e){
			//do something
			logger.warn("something is wrong ExecutionException");
		}
		
		return results;
	}
	
	private Campaigns taskProcessPerThread(Campaigns item){
		
		int budget = 0;
		boolean isSmooth = true;
		String response = proxy.getCampaignBudget(item.getNick(), item.getCampaignId());
		if(response != null && response.contains("simba_campaign_budget_get_response")){
			JSONObject object = JSON.parseObject(response);
			JSONObject objRes = object==null? null : object.getJSONObject("simba_campaign_budget_get_response");
			JSONObject objItem = objRes==null? null : objRes.getJSONObject("campaign_budget");
			budget = objItem == null? 0 : objItem.getInteger("budget");	
			isSmooth = objItem == null? true : objItem.getBoolean("is_smooth");
		}
		item.setBudget(budget);
		String useSmooth = isSmooth ? "true": "false";
		item.setUseSmooth(useSmooth);
		
		String area = "";
		response = proxy.getCampaignArea(item.getNick(), item.getCampaignId());
		if(response != null && response.contains("simba_campaign_area_get_response")){
			JSONObject object = JSON.parseObject(response);
			JSONObject objRes = object==null? null : object.getJSONObject("simba_campaign_area_get_response");
			JSONObject objItem = objRes==null? null : objRes.getJSONObject("campaign_area");
			area = objItem == null? "" : objItem.getString("area");	
		}
		item.setArea(area);	
		
		String campaignPlatform = "";
		response = proxy.getCampaignPlatform(item.getNick(), item.getCampaignId());
		if(response != null && response.contains("simba_campaign_platform_get_response")){
			JSONObject object = JSON.parseObject(response);
			JSONObject objRes = object==null? null : object.getJSONObject("simba_campaign_platform_get_response");
			campaignPlatform = objRes==null ? "" : objRes.getString("campaign_platform");	
		}
		item.setPlatform(campaignPlatform);
		
		String schedule = "";
		response = proxy.getCampaignSchedule(item.getNick(), item.getCampaignId());
		if(response != null && response.contains("simba_campaign_schedule_get_response")){
			JSONObject object = JSON.parseObject(response);
			JSONObject objRes = object==null? null : object.getJSONObject("simba_campaign_schedule_get_response");
			JSONObject objItems = objRes==null? null : objRes.getJSONObject("campaign_schedule");
			schedule = objItems == null ? "" : objItems.getString("schedule");
		}
		item.setSchedule(schedule);	
		
		//String incrementalPrice = "";
		//response = proxy.getCampaignDemoGraphics(item.getNick(), item.getCampaignId());
		//if(response != null && response.contains("simba_nonsearch_demographics_get_response")){
		//	JSONObject object = JSON.parseObject(response);
		//	JSONObject objRes = object==null? null : object.getJSONObject("simba_nonsearch_demographics_get_response");
		//	JSONObject jsonList = objRes==null? null : objRes.getJSONObject("demographic_setting_list");
		//	incrementalPrice = jsonList==null ? "" : jsonList.toJSONString();
		//}
		//item.setIncrementalPrice(incrementalPrice);
				
		return item;
	}

}